﻿using System;
using System.Collections.Generic;
using FairyGUI;
using UnityEngine;
using Object = UnityEngine.Object;

namespace ZFramework.Runtime
{
    public class FguiUIPackageMgr : FrameworkManagerBase
    {
        private LoadAssetCallbacks _loadAssetCallbacks;    //加载fgui中的texture的回调
        private LoadAssetCallbacks _loadDescTextAssetCallbacks;    //加载fgui描述文件的回调
        private LoadAssetCallbacks _loadPackageAssetCallbacks;    //加载asset文件的回调
        private IResourceMgr _resourceMgr;

        private Dictionary<string, FguiUIPackageInfo> _pkgName2UIPackageInfos;
        private Dictionary<Texture, string> _atlasTexture2Path;
        
        private string _packageInfoAssetPath = "Assets/Res/UI/PackageInfoAsset/FguiPackageInfoAsset.asset";
        private bool _loadPackageInfoAsset;

        private HashSet<string> _cachedDepPkgNames;
        private float _autoReleaseInterval = 10;
        private int _capacity = 1;
        private float _expireTime;
        private float _autoReleaseTime = 0; 
        
        public bool IsPreloadReady
        {
            get
            {
                if (!_loadPackageInfoAsset)
                    return false;
                
                foreach (var item in _pkgName2UIPackageInfos)
                {
                    var info = item.Value;
                    if (!info.LoadFlag)
                    {
                        return false;
                    }
                }

                return true;
            }
        }

        public void PreLoad()
        {
            _resourceMgr.LoadAssetAsync<FguiAllPackageInfoAsset>(_packageInfoAssetPath, _loadPackageAssetCallbacks);
        }

        public void InitUIPackages()
        {
            if (!IsPreloadReady)
            {
                Log.Error("资源还没加载完，请稍后再初始化UIPackage");
                return;
            }

            foreach (var item in _pkgName2UIPackageInfos)
            {
                var info = item.Value;
                var descAsset = info.LoadedPkgDescTextAsset;
                var pkg = UIPackage.AddPackage(descAsset.bytes, "", LoadUIExtensions);
                info.Package = pkg;
            }
            
            //分析所有依赖信息
            foreach (var item in _pkgName2UIPackageInfos)
            {
                var info = item.Value;
                var pkgName = info.PkgName;
                GetDependencies(pkgName);
                foreach (var depPkgName in _cachedDepPkgNames)
                {
                    info.AddDependence(depPkgName);
                }
            }
        }

        protected override void Awake()
        {
            base.Awake();
            _autoReleaseTime = 0;
            _atlasTexture2Path = new Dictionary<Texture, string>();
            _pkgName2UIPackageInfos = new Dictionary<string, FguiUIPackageInfo>();
            _cachedDepPkgNames = new HashSet<string>();
            _loadPackageInfoAsset = false;
            _loadAssetCallbacks = new LoadAssetCallbacks(LoadAssetSuccessCallback, LoadAssetFailureCallback);
            _loadDescTextAssetCallbacks = new LoadAssetCallbacks(LoadDescTextAssetSuccessCallback, LoadDescTextAssetFailureCallback);
            _loadPackageAssetCallbacks = new LoadAssetCallbacks(LoadPackageAssetSuccessCallback, LoadPackageAssetFailureCallback);
        }

        void Start()
        {
            _resourceMgr = FrameworkManagerManager.GetManager<YooResourceMgr>();
            if (_resourceMgr == null)
            {
                Log.Error("YooResourceMgr is invalid.");
                return;
            }
            NTexture.CustomDestroyMethod += CustomDestroyNTexture;
        }
        
        public override void Shutdown()
        {
            NTexture.CustomDestroyMethod -= CustomDestroyNTexture;
        }
        
        public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
        {
            if (_pkgName2UIPackageInfos.Count <= 0)
                return;
            _autoReleaseTime += Time.unscaledDeltaTime;
            if (_autoReleaseTime < _autoReleaseInterval)
            {
                return;
            }

            Release();
        }

        public void AddUIPackageRefCount(string pkgName)
        {
            var info = GetUIPackageInfoByPkgName(pkgName);
            if (info == null)
                return;
            var depList = info.Dependecies;

            foreach (var depPkgName in depList)
            {
                var depInfo = GetUIPackageInfoByPkgName(depPkgName);
                if(depInfo == null)
                {
                    continue;
                }

                depInfo.AddRefCount();
            }
        }

        public void SubUIPackageRefCount(string pkgName)
        {
            var info = GetUIPackageInfoByPkgName(pkgName);
            if (info == null)
                return;
            var depList = info.Dependecies;

            foreach (var depPkgName in depList)
            {
                var depInfo = GetUIPackageInfoByPkgName(depPkgName);
                if(depInfo == null)
                {
                    continue;
                }

                depInfo.SubRefCount();
            }
        }

        private void CustomDestroyNTexture(Texture texture)
        {
            Log.Error("卸载ui 资源 texture=={0}", texture);
            if (!_atlasTexture2Path.TryGetValue(texture, out var path))
            {
                Log.Error("找不到texture的路径， {0}", texture);
                return;
            }

            _atlasTexture2Path.Remove(texture);
            _resourceMgr.UnloadAsset(path);
        }
        
        private void LoadUIExtensions(string atlasName, string extension, Type type, PackageItem item)
        {
            if (type != typeof(Texture))
                return;
            var pkgName = item.owner.name;
            var info = GetUIPackageInfoByPkgName(pkgName);
            if (info == null)
                return;
            info.AtlasLoadStatus = FguiUIPackageInfo.EnumAtlasLoadStatus.Loaded;
            string rootPath = info.AtlasRootPath;
            //item.owner.name是packageName，atlasName是“atlas0”或者“atlas1”,extension是“.png”
            string extPath = Utility.Text.Format("{0}/{1}_{2}{3}", rootPath, pkgName, atlasName, extension);
            _resourceMgr.LoadAssetAsync<Object>(extPath, _loadAssetCallbacks, item);
            Log.Error("加载ui 资源 path=={0}", extPath);
        }
        
        private void LoadAssetSuccessCallback(string assetPath, Object asset, float duration, object userData)
        {
            var item = userData as PackageItem;
            if (item == null)
                return;
            var texture = asset as Texture;
            if (texture == null)
                return;
            _atlasTexture2Path[texture] = assetPath;
            item.owner.SetItemAsset(item, asset, DestroyMethod.Custom);
        }
        
        private void LoadAssetFailureCallback(string assetName, string errorMessage, object userData)
        {
            Log.Error("FGUI中加载{0}失败, errorMessage=={1}", assetName, errorMessage);
        }
        
        private void LoadPackageAssetSuccessCallback(string assetPath, Object asset, float duration, object userData)
        {
            var packageInfo = asset as FguiAllPackageInfoAsset;
            if (packageInfo == null)
            {
                Log.Error("无法加载assetPath=={0}", assetPath);
                return;
            }

            var packageNames = packageInfo.PackageNames;
            var atlasRootPaths = packageInfo.AtlasRootPaths;
            var descAssetPaths = packageInfo.DescAssetPaths;
            var count = packageNames.Length;
            if (count <= 0)
                return;
            for (int i = 0; i < count; i++)
            {
                var pkgName = packageNames[i];
                var descAssetPath = descAssetPaths[i];
                var atlasRootPath = atlasRootPaths[i];
                var info = FguiUIPackageInfo.Create(pkgName);
                info.AtlasRootPath = atlasRootPath;
                info.DescTextAssetPath = descAssetPath;
                _pkgName2UIPackageInfos[pkgName] = info;
                _resourceMgr.LoadAssetAsync<TextAsset>(descAssetPath, _loadDescTextAssetCallbacks, pkgName);
            }
            _loadPackageInfoAsset = true;
        }
        
        private void LoadPackageAssetFailureCallback(string assetName, string errorMessage, object userData)
        {
            Log.Error("FGUI中加载{0}失败, errorMessage=={1}", assetName, errorMessage);
        }
        
        private void LoadDescTextAssetSuccessCallback(string assetPath, Object asset, float duration, object userData)
        {
            var pkgName = userData as string;
            if (pkgName == null)
                return;
            var info = GetUIPackageInfoByPkgName(pkgName);
            if (info == null)
                return;

            info.LoadFlag = true;
            info.LoadedPkgDescTextAsset = (TextAsset)asset;
        }
        
        private void LoadDescTextAssetFailureCallback(string assetName, string errorMessage, object userData)
        {
            Log.Error("LoadDescTextAssetFailureCallback FGUI 中加载{0}失败, errorMessage=={1}", assetName, errorMessage);
            var pkgName = userData as string;
            if (pkgName == null)
                return;

            var info = GetUIPackageInfoByPkgName(pkgName);
            if (info == null)
                return;
            info.LoadFlag = false;
        }

        private FguiUIPackageInfo GetUIPackageInfoByPkgName(string pkgName)
        {
            if (_pkgName2UIPackageInfos.TryGetValue(pkgName, out var info))
            {
                return info;
            }

            return null;
        }

        private void GetDependencies(string pkgName)
        {
            _cachedDepPkgNames.Clear();
            AddDependencies(pkgName, _cachedDepPkgNames);
        }

        private void AddDependencies(string pkgName, HashSet<string> allPkgNames)
        {
            if (allPkgNames.Contains(pkgName))
                return;
            var info = GetUIPackageInfoByPkgName(pkgName);
            if (info == null)
                return;
            var pkg = info.Package;

            allPkgNames.Add(pkgName);
            var deps = pkg.dependencies;
            if (deps != null && deps.Length > 0)
            {
                foreach (var item in deps)
                {
                    var depPkgName = item["name"];
                    AddDependencies(depPkgName, allPkgNames);
                }
            }
        }

        private void Release()
        {
            foreach (var item in _pkgName2UIPackageInfos)
            {
                var info = item.Value;
                info.Release();
            }

            _autoReleaseTime = 0;
        }
    }
}